/* MAA Message Authenticator Algorithm */
/* compiled and run under Turbo C++ version 1.01  */

#include <stdio.h>
#include <time.h>
#include <dos.h>

#define BIT31 0x80000000L
#define FFFF  0xFFFFL

unsigned long a,b,c,d,l,m,s,t,u,v,w,x,y,z,pat,sum,t4,t5,v0,x0,y0;

int menu1()
{
  int m;
  printf("\n 1 Test small functions (Tables 1, 2)\n");
  printf(" 2 Test prelude (Table 3)\n");
  printf(" 3 Test main (Table 4)\n");
  printf(" 4 Test prelude + main + coda (Table 5)\n");
  printf(" 5 Test and timing of repeated message blocks (Table 6)\n");
  printf(" 6 Quit \n");
  scanf("%d", &m);
  return(m);
}  /* menu1 */

void mul32(a, b)
unsigned long a, b;
/* unsigned 64 bit product in u (upper) and l (lower) */
{
  unsigned long p1, p2, p3, sum;
  p1 = (a & FFFF) * (b & FFFF);
  p2 = (a & FFFF) * (b >> 16);
  p3 = (a >> 16) * (b & FFFF);
  sum = (p1 >> 16) + (p2 & FFFF) + (p3 & FFFF);
  l = (p1 & FFFF) + (sum << 16);
  u = (sum >> 16) + (p2 >> 16) + (p3 >> 16) + ( (a >> 16) * (b >> 16) );
}  /* mul32 */

unsigned long mul1(a, b)
unsigned long a, b;
{
  unsigned long s, car;
  mul32(a, b);
  s = u + l;
  car = ( (u ^ l) & BIT31) ? !(s & BIT31) : (u & BIT31) != 0;
  return(s + car);
}  /* mul1  */

unsigned long mul2(a, b)
unsigned long a, b;
{
  unsigned long d, f, s, car;
  mul32(a, b);
  d = u + u; car = (u & BIT31) != 0;
  f = d + car + car;
  s = f + l;
  car = ( (f ^ l) & BIT31) ? !(s & BIT31) : (f & BIT31) != 0;
  return(s + car + car);
}  /*  mul2  */

unsigned long mul2a(a, b)
unsigned long a, b;
{
  unsigned long d, s, car;
  mul32(a, b);
  d = u + u;
  s = d + l;
  car = ( (d ^ l) & BIT31) ? !(s & BIT31) : (d & BIT31) != 0;
  return(s + car + car);
}  /*  mul2a  */

void byt(x, y)
unsigned long x, y;
{
  unsigned long p;
  int i, b[8];
  for(i = 3; i >= 0; i--)
  { b[i] = x & 255; b[i + 4] = y & 255;
    x = x >> 8; y = y >> 8;
  }
  p = 0;
  for(i = 0; i < 8; i++)
  { p = p + p;
    if (b[i] == 0) {p += 1; b[i] = p;}
    else if (b[i] == 255) {p += 1; b[i] = 255 - p;}
  }
  pat = p; x = 0; y = 0;
  for (i = 0; i < 4; i++) {x = (x << 8) + b[i]; y = (y << 8) + b[i + 4];}
  u = x; l = y;
}  /*  byt  */

void prelude(x, y, test)
unsigned long x, y;
int test;
{
  unsigned long j1[10], j2[10], k1[10], k2[10], h[10];
  unsigned long p, q;

  byt(x, y);
  j1[0] = u; k1[0] = l; p = pat;

  if (test > 0)
  { j1[0] = x; k1[0] = y;
    printf(" Enter  P (2 hex): ");
    scanf("%X", &p);
  }
  q = (p + 1) * (p + 1);
  j1[2] = mul1(j1[0], j1[0]); j2[2] = mul2(j1[0], j1[0]);
  j1[4] = mul1(j1[2], j1[2]); j2[4] = mul2(j2[2], j2[2]);
  j1[6] = mul1(j1[2], j1[4]); j2[6] = mul2(j2[2], j2[4]);
  j1[8] = mul1(j1[2], j1[6]); j2[8] = mul2(j2[2], j2[6]);

  h[4] = j1[4] ^ j2[4]; h[6] = j1[6] ^ j2[6]; h[8] = j1[8] ^ j2[8];

  printf("H4= %08lX \n", h[4]);
  printf("H6= %08lX \n", h[6]);
  printf("H8= %08lX \n", h[8]);

  k1[2] = mul1(k1[0], k1[0]); k2[2] = mul2(k1[0], k1[0]);
  k1[4] = mul1(k1[2], k1[2]); k2[4] = mul2(k2[2], k2[2]);
  k1[5] = mul1(k1[0], k1[4]); k2[5] = mul2(k1[0], k2[4]);
  k1[7] = mul1(k1[2], k1[5]); k2[7] = mul2(k2[2], k2[5]);
  k1[9] = mul1(k1[2], k1[7]); k2[9] = mul2(k2[2], k2[7]);

  h[0] = k1[5] ^ k2[5]; h[5] = mul2(h[0], q);
  h[7] = k1[7] ^ k2[7]; h[9] = k1[9] ^ k2[9];

  printf("H0= %08lX \n", h[0]);
  printf("H5= %08lX \n", h[5]);
  printf("H7= %08lX \n", h[7]);
  printf("H9= %08lX \n", h[9]);

  byt(h[4], h[5]);
  x0 = u; y0 = l;

  printf("X0= %08lX", x0);
  printf("  Y0= %08lX", y0);
  printf("  PAT= %08lX \n", pat);

  byt(h[6], h[7]);
  v0 = u; w = l;

  printf("V0= %08lX", v0);
  printf("   W= %08lX", w);
  printf("  PAT= %08lX \n", pat);

  byt(h[8], h[9]);
  s = u; t = l;

  printf(" S= %08lX", s);
  printf("   T= %08lX", t);
  printf("  PAT= %08lX \n", pat);

} /* prelude */


void abcd()
{ a = 0x02040801L;
  b = 0x00804021L;
  c = 0xBFEF7FDFL;
  d = 0x7DFEFBFFL;
  printf("A= %08lX \n", a);
  printf("B= %08lX \n", b);
  printf("C= %08lX \n", c);
  printf("D= %08lX \n", d);
} /* abcd */

void inmain()
{ abcd();
  printf("Press 1 to enter A, B, C, D, V, W, X0, Y0, M ");
  printf("else <RET> for standard ABCD\n");
  if ( ( getch() ) == '1' )
  { printf("Enter  A (8 hex): "); scanf("%lX", &a);
    printf("Enter  B (8 hex): "); scanf("%lX", &b);
    printf("~(8)=FFFFFFF7, ~(4)=FFFFFFFB, ~(6)=FFFFFFF9, ~(3)=FFFFFFFC \n");
    printf("~(1)=FFFFFFFE, ~(2*)=7FFFFFFD, ~(2)=FFFFFFFD \n");
    printf("Enter  C (8 hex): "); scanf("%lX", &c);
    printf("Enter  D (8 hex): "); scanf("%lX", &d);
    printf("Enter  V (8 hex): "); scanf("%lX", &v);
    printf("Enter  W (8 hex): "); scanf("%lX", &w);
    printf("Enter X0 (8 hex): "); scanf("%lX", &x);
    printf("Enter Y0 (8 hex): "); scanf("%lX", &y);
    printf("Enter  M (8 hex): "); scanf("%lX", &m);
  }
} /* inmain */

void mainloop1(m)
unsigned long m;
{ unsigned long e, f, g;
  printf("Results are in hexadecimal \n");
  v = (v & BIT31)? (v << 1) | 1L: v << 1;   /*  v = cyc(v);  */
                   printf("V= %08lX \n", v);
  e = v ^ w;       printf("E= %08lX \n", e);
  x = x ^ m;       printf("X= %08lX ", x);
  y = y ^ m;       printf("  Y= %08lX \n", y);
  f = e + y;       printf("F= %08lX ", f);
  g = e + x;       printf("  G= %08lX \n", g);
  f = f | a;       printf("F= %08lX ", f);
  g = g | b;       printf("  G= %08lX \n", g);
  f = f & c;       printf("F= %08lX ", f);
  g = g & d;       printf("  G= %08lX \n", g);
  x = mul1(x, f);  printf("X= %08lX ", x);
  y = mul2(y, g);  printf("  Y= %08lX \n", y);
} /* mainloop1 */

void mainloop2(m)
unsigned long m;
/* as mainloop1 without printf. Mul1, Mul2a copied to save time */
{ unsigned long e, f, g, s;
  int car;
  v = (v & BIT31)? (v << 1) | 1L: v << 1;          /* v = cyc(v); */
  e = v ^ w;
  x = x ^ m;      y = y ^ m;
  f = e + y;      g = e + x;
  f = f | a;      g = g | b;
  f = f & c;      g = g & d;

                                                   /* x = mul1(x, f); */
  mul32(x, f);
  s = u + l;
  car = ((u ^ l) & BIT31)? !(s & BIT31) : (u & BIT31) != 0;
  x = (car) ? s + 1L : s;
                                                   /* y = mul2a(y, g); */
  mul32(y, g);
  g = u + u;
  s = g + l;
  car = ((g ^ l) & BIT31)? !(s & BIT31) : (g & BIT31) != 0;
  y = (car)? s + 2L : s;
} /* mainloop2 */

/* NOTE. Time measurement is not standard C, hence may not be portable.
   Guesstime + fracsec uses the 1 second clock of MS-DOS.
   Works with Turbo and Zorland compilers.  */

unsigned long fracsec()
{ unsigned long i;
  i = 0; t5 = time(NULL);
  do i++;          /* count while waiting for next second  */
  while (time(NULL) == t5);
  return(i);
} /* fracsec */

float guesstime()
/* Uses fracsec and time (which counts whole seconds).
   Probably most portable, may be least accurate if interrupted.
   OK for Turbo and Zorland versions 1 and 2.   */
{ unsigned long i, j, secs;
  t4 = t5;
  i = fracsec(); secs = t5 - t4 - 1; j = fracsec();
  if (i > j)  i = j;
  return(secs + (j - i)/(float) j);
} /* guesstime */

void main()
{ unsigned long i, t1, t2;
  long ti;
  int nn, ii, choice, disp, car;
  float tf;
  t5 = 0;
  do
  { choice = menu1();
    printf("Note all results (except time) are in hexadecimal \n");
    switch(choice)
    { case 1:
      { printf(" Enter X (8 hex): "); scanf("%lX", &x);
        printf(" Enter Y (8 hex): "); scanf("%lX", &y);
        sum = x + y;
        printf("Sum= %08lX", sum);
        car = ((x ^ y) & BIT31) ? !(sum & BIT31) : (x & BIT31) != 0;
        printf("  Carry= %X \n", car);
        sum = x + x;
        car = (x & BIT31) != 0;
        printf("2*X= %08lX", sum);
        printf("  Carry= %X \n", car);
        printf("Product= %08lX\n", x * y);
        mul32(x, y);
        printf("mul32 upper= %08lX", u);
        printf("  mul32 lower= %08lX\n", l);
        printf("  cyc=  %08lX\n", ((x << 1) | ((x & BIT31) >> 31)) );
        printf(" mul1=  %08lX\n", mul1(x, y) );
        printf(" mul2=  %08lX\n", mul2(x, y) );
        printf("mul2a=  %08lX\n", mul2a(x, y) );
        byt(x, y);
        printf("byt upper= %08lX", u);
        printf("  byt lower= %08lX\n", l);
        printf("  pat=  %X\n", pat);
      }; break;
      case 2:
      { printf(" Enter J1 (8 hex): "); scanf("%lX", &x);
        printf(" Enter K1 (8 hex): "); scanf("%lX", &y);
        prelude(x, y, 1);
      }; break;
      case 3:
      { inmain();
        mainloop1(m);
        z = x ^ y; printf("Z= %08lX\n", z);
      }; break;
      case 4:
      { printf(" Enter J (8 hex): "); scanf("%lX", &x);
        printf(" Enter K (8 hex): "); scanf("%lX", &y);
        prelude(x, y, 0);
        x = x0; y = y0; v = v0;
        abcd();
        do
        { printf(" Enter M (8 hex): "); scanf("%lX", &m);
          mainloop2(m);
          printf("X= %08lX", x); printf("  Y= %08lX\n", y);
          printf("Press 1 for coda else <RET> for another message \n");
        } while ( (getch() ) != '1' );
        mainloop2(s);
        printf("X= %08lX", x); printf("  Y= %08lX\n", y);
        mainloop2(t);
        printf("X= %08lX", x); printf("  Y= %08lX\n", y);
        z = x ^ y; printf("Z= %08lX\n", z);
      }; break;
      case 5:
      { printf(" Enter J (8 hex): "); scanf("%lX", &x);
        printf(" Enter K (8 hex): "); scanf("%lX", &y);
        prelude(x, y, 0);
        x = x0; y = y0; v = v0;
        abcd();
        printf(" Enter M (8 hex): "); scanf("%lX", &m);
        printf(" Enter number of repeated message blocks: ");
        scanf("%d", &nn);
        printf(" Press 1 to display X, Y values else <RET> to measure time\n");
        disp = getch();
        if (disp == '1')
        { for (ii = 1; ii <= nn; ii++)
          { mainloop2(m);
            printf("X= %08lX", x); printf("  Y= %08lX\n", y);
          }
          printf(" Press a key to proceed \n"); disp = getch();
        }
        else
        { i = fracsec();
          for (ii = 1; ii <= nn; ii++)
          { mainloop2(m);
          }
          tf = guesstime();
          printf("Seconds in mainloop (guesstime)= %6.2f\n", tf); i = i;
          printf(" Milliseconds per message block= %6.2f\n", tf * 1000.0 / nn);
        }
        printf("X= %08lX", x); printf("  Y= %08lX\n", y);
        mainloop2(s);
        printf("X= %08lX", x); printf("  Y= %08lX\n", y);
        mainloop2(t);
        printf("X= %08lX", x); printf("  Y= %08lX\n", y);
        z = x ^ y; printf("Z= %08lX \n", z);
      }; break;
    }
  } while (choice != 6);
} /* main */

